package monitor

import (
	"fmt"
	"strconv"
	"time"

	influx "github.com/influxdata/influxdb/client/v2"
)

var (
	dbClient influx.Client = nil
	dbName   string        = "appmon"
)

func newCPUPoint(id int, usaged float32, t time.Time) (*influx.Point, error) {

	tags := make(map[string]string)
	tags["id"] = strconv.Itoa(id)

	fields := make(map[string]interface{})
	fields["usaged"] = usaged

	return influx.NewPoint("cpu", tags, fields, t)
}

func newMemPoint(free, cache uint64, usaged float32, t time.Time) (*influx.Point, error) {

	tags := make(map[string]string)

	fields := make(map[string]interface{})
	fields["usaged"] = usaged
	fields["free"] = int(free)
	fields["cache"] = int(cache)

	return influx.NewPoint("mem", tags, fields, t)
}

func newProcessPoint(name string, pid int, cpu, mem float32, memSize uint64, t time.Time) (*influx.Point, error) {

	tags := make(map[string]string)
	tags["name"] = name
	tags["pid"] = strconv.Itoa(pid)

	fields := make(map[string]interface{})
	fields["mem"] = mem
	fields["cpu"] = cpu
	fields["memSize"] = int(memSize)

	return influx.NewPoint("process", tags, fields, t)
}

func influxQuery(client influx.Client, cmd string, db string) (res []influx.Result, err error) {
	q := influx.Query{
		Command:  cmd,
		Database: db,
	}
	if response, err := client.Query(q); err == nil {
		if response.Error() != nil {
			return res, response.Error()
		}
		res = response.Results
	} else {
		return res, err
	}
	return res, nil
}

// HandleInfluxdb is for saving data to influxdb
func HandleInfluxdb(s *Sample) {

	if dbClient == nil {
		return
	}

	bp, err := influx.NewBatchPoints(influx.BatchPointsConfig{Precision: "ns", Database: dbName})

	// Check if dbClient ok
	point, err := newCPUPoint(0, s.GetCPULoad(0), s.GetCPUTimeStamp())

	if err == nil {
		bp.AddPoint(point)
	}

	point, err = newMemPoint(s.GetMemFreeSize(), s.GetMemCacheSize(), s.GetMemUsaged(), s.GetMemTimeStamp())

	if err == nil {
		bp.AddPoint(point)
	}

	pids := s.GetProcessPids()

	for _, p := range pids {

		point, err = newProcessPoint(s.GetProcessName(p), p,
			s.GetProcessCPULoad(p), s.GetProcessMemUsaged(p), s.GetProcessMemSize(p), s.GetProcessTimeStamp(p))

		if err == nil {
			bp.AddPoint(point)
		}
	}

	tn := time.Now()
	err = dbClient.Write(bp)
	if err != nil {
		fmt.Printf("Influx write failed: %v\n", err)
	} else {
		takes := time.Now().Sub(tn)
		fmt.Printf("Influxdb write ok, takes %v\n", takes)
	}

}

// ConnectToInfluxdb create database client
func ConnectToInfluxdb(addr string, db string) error {

	client, err := influx.NewHTTPClient(influx.HTTPConfig{Addr: addr})

	if err != nil {
		fmt.Println("Create influxdb client failed:", err)
		return err
	}

	// check if alive
	t, s, err := client.Ping(time.Millisecond * 500)

	if err != nil {
		fmt.Printf("Influxdb ping failed, not running? %v\n", err)
		client.Close()
		return err
	}

	fmt.Printf("Influxdb ping return %v %s\n", t, s)

	// create database
	_, err = influxQuery(client, fmt.Sprintf("create database %s", db), "")

	if err != nil {
		fmt.Printf("Create database failed:%v\n", err)
		client.Close()
		return err
	}

	dbClient = client
	dbName = db
	return nil
}
